package com.thinkit.directive.render;

import com.thinkit.directive.emums.LangEnum;
import com.thinkit.utils.utils.Checker;
import freemarker.core.Environment;
import freemarker.ext.beans.BeanModel;
import freemarker.template.*;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

public class Wrapper  {

    private Environment environment;
    private Map<String, TemplateModel> map;
    private TemplateModel[] templateModels;
    private TemplateDirectiveBody templateDirectiveBody;
    private Boolean isRender = false;

    public Wrapper(Environment environment, Map map, TemplateModel[] templateModels, TemplateDirectiveBody templateDirectiveBody){
        this.environment = environment;
        this.map = map;
        this.templateModels = templateModels;
        this.templateDirectiveBody = templateDirectiveBody;
        this.environment = environment;
    }

    public <T> T getParam(String name, LangEnum langEnum) throws TemplateModelException {
         switch (langEnum){
             case STRING:
                 SimpleScalar simpleScalar = getStringModel(name,langEnum);
                 if(Checker.BeNotNull(simpleScalar)){
                     return (T) simpleScalar.getAsString();
                 }
                 break;
             case BOOLEAN:
                 TemplateBooleanModel booleanModel = getBooleanModel(name,langEnum);
                 if(Checker.BeNotNull(booleanModel)){
                     return (T) Boolean.valueOf(booleanModel.getAsBoolean());
                 }
                 break;
             case DATE:
                 TemplateDateModel dateModel = getDateModel(name,langEnum);
                 if(Checker.BeNotNull(dateModel)){
                     return (T) dateModel.getAsDate();
                 }
                 break;
             case MAP:
                 TemplateHashModel hashModel = getMapModel(name,langEnum);
                 if(Checker.BeNotNull(hashModel)){
                     return (T) hashModel;
                 }
                 break;
             case BEAN:
                 Object object = getBeanModel(name,langEnum);
                 if(Checker.BeNotNull(object)){
                     return (T) object;
                 }
                 break;
             case STRING_ARRAY:
                 TemplateSequenceModel seqStringModel = getArrayModel(name,langEnum);
                 if(Checker.BeNotNull(seqStringModel)){
                     return (T) formatArray(seqStringModel,langEnum);
                 }
                 break;
             case LONG_ARRAY:
                 TemplateSequenceModel seqLongModel = getArrayModel(name,langEnum);
                 if(Checker.BeNotNull(seqLongModel)){
                     return (T) formatArray(seqLongModel,langEnum);
                 }
                 break;
             case INTEGER_ARRAY:
                 TemplateSequenceModel seqIntegerModel = getArrayModel(name,langEnum);
                 if(Checker.BeNotNull(seqIntegerModel)){
                     return (T) formatArray(seqIntegerModel,langEnum);
                 }
                 break;
             default:
                 if(LangEnum.isNumber(langEnum)){
                     Number number = getNumberModel(name,langEnum).getAsNumber();
                     if(Checker.BeNotNull(number)){
                         return (T) formatVal(number,langEnum);
                     }
                 }
         }
        return null;
    }


    private <T> T formatArray(TemplateSequenceModel sequenceModel,LangEnum langEnum) throws TemplateModelException {
        if(LangEnum.STRING_ARRAY.equals(langEnum)){
            String[] values = new String[sequenceModel.size()];
            for (int i = 0; i < sequenceModel.size(); i++) {
                values[i] = toStringArray(sequenceModel.get(i));
            }
            return (T) values;
        }else if(LangEnum.LONG_ARRAY.equals(langEnum)){
            Long[] values = new Long[sequenceModel.size()];
            for (int i = 0; i < sequenceModel.size(); i++) {
                values[i] = toLongArray(sequenceModel.get(i));
            }
            return (T) values;
        }else if(LangEnum.INTEGER_ARRAY.equals(langEnum)){
            Integer[] values = new Integer[sequenceModel.size()];
            for (int i = 0; i < sequenceModel.size(); i++) {
                values[i] = toIntegerArray(sequenceModel.get(i));
            }
            return (T) values;
        }
        return null;
    }


    public static String toStringArray(TemplateModel templateModel) throws TemplateModelException {
        if (Checker.BeNotNull(templateModel)) {
            if (templateModel instanceof TemplateSequenceModel) {
                toStringArray(((TemplateSequenceModel) templateModel).get(0));
            }
            if (templateModel instanceof TemplateScalarModel) {
                return ((TemplateScalarModel) templateModel).getAsString();
            } else if ((templateModel instanceof TemplateNumberModel)) {
                Number number = ((TemplateNumberModel) templateModel).getAsNumber();
                if(Checker.BeNotNull(number)){
                    return number.toString();
                }
            }
        }
        return null;
    }

    public static Long toLongArray(TemplateModel templateModel) throws TemplateModelException {
        if (Checker.BeNotNull(templateModel)) {
            if (templateModel instanceof TemplateSequenceModel) {
                toLongArray(((TemplateSequenceModel) templateModel).get(0));
            }
            if (templateModel instanceof TemplateScalarModel) {
                return Long.valueOf(((TemplateScalarModel) templateModel).getAsString());
            } else if ((templateModel instanceof TemplateNumberModel)) {
                Number number = ((TemplateNumberModel) templateModel).getAsNumber();
                if(Checker.BeNotNull(number)){
                    return number.longValue();
                }
            }
        }
        return null;
    }

    public static Integer toIntegerArray(TemplateModel templateModel) throws TemplateModelException {
        if (Checker.BeNotNull(templateModel)) {
            if (templateModel instanceof TemplateSequenceModel) {
                toIntegerArray(((TemplateSequenceModel) templateModel).get(0));
            }
            if (templateModel instanceof TemplateScalarModel) {
                return Integer.valueOf(((TemplateScalarModel) templateModel).getAsString());
            } else if ((templateModel instanceof TemplateNumberModel)) {
                Number number = ((TemplateNumberModel) templateModel).getAsNumber();
                if(Checker.BeNotNull(number)){
                    return number.intValue();
                }
            }
        }
        return null;
    }

    private Number formatVal(Number number,LangEnum langEnum){
        if(LangEnum.LONG.equals(langEnum)){
            return Long.valueOf( number.longValue());
        }else if(LangEnum.INTEGER.equals(langEnum)){
            return Integer.valueOf( number.intValue());
        }else if(LangEnum.DOUBLE.equals(langEnum)){
            return Double.valueOf( number.doubleValue());
        }else if(LangEnum.FLOAT.equals(langEnum)){
            return Float.valueOf( number.floatValue());
        }else if(LangEnum.SHORT.equals(langEnum)){
            return Short.valueOf( number.shortValue());
        }
        return number;
    }


    public <T> T getParam(String name,T defval, LangEnum langEnum) throws TemplateModelException {
        T value = getParam(name,langEnum);
        return Checker.BeNotNull(value)?value:defval;
    }



    private SimpleScalar getStringModel(String name,LangEnum langEnum){
        TemplateModel templateModel = map.get(name);
        if(Checker.BeNotNull(templateModel)){
            if(LangEnum.STRING.equals(langEnum)){
                return (SimpleScalar) templateModel;
            }
        }
        return null;
    }

    private TemplateBooleanModel getBooleanModel(String name, LangEnum langEnum){
        TemplateModel templateModel = map.get(name);
        if(Checker.BeNotNull(templateModel)){
            if(LangEnum.BOOLEAN.equals(langEnum)){
                return (TemplateBooleanModel) templateModel;
            }
        }
        return null;
    }

    private SimpleNumber getNumberModel(String name,LangEnum langEnum){
        TemplateModel templateModel = map.get(name);
        if(Checker.BeNotNull(templateModel)){
            if(LangEnum.isNumber(langEnum)){
                return (SimpleNumber) templateModel;
            }
        }
        return null;
    }

    private TemplateDateModel getDateModel(String name,LangEnum langEnum){
        TemplateModel templateModel = map.get(name);
        if(Checker.BeNotNull(templateModel)){
            if(LangEnum.isNumber(langEnum)){
                return (TemplateDateModel) templateModel;
            }
        }
        return null;
    }

    private TemplateHashModel getMapModel(String name,LangEnum langEnum){
        TemplateModel templateModel = map.get(name);
        if(Checker.BeNotNull(templateModel)){
            if(LangEnum.MAP.equals(langEnum)){
                if(templateModel instanceof  TemplateHashModel){
                    return (TemplateHashModel) templateModel;
                }
            }
        }
        return null;
    }

    private Object getBeanModel(String name,LangEnum langEnum){
        TemplateModel templateModel = map.get(name);
        if(Checker.BeNotNull(templateModel)){
            if(LangEnum.BEAN.equals(langEnum)){
                if (templateModel instanceof BeanModel) {
                    return ((BeanModel) templateModel).getWrappedObject();
                }
            }
        }
        return null;
    }

    private TemplateSequenceModel getArrayModel(String name,LangEnum langEnum){
        TemplateModel templateModel = map.get(name);
        if(Checker.BeNotNull(templateModel)){
            if(LangEnum.isArray(langEnum)){
                return (SimpleSequence) templateModel;
            }
        }
        return null;
    }


    public Wrapper construct(Map<String, Object> params) throws IOException, TemplateException {
        if (!isRender) {
            Map<String, TemplateModel> reduceMap = buildModel(params);
            if (Checker.BeNotNull(templateDirectiveBody)) {
                templateDirectiveBody.render(environment.getOut());
            }
            if(Checker.BeNotNull(reduceMap)){
                Environment.Namespace namespace = environment.getCurrentNamespace();
                namespace.putAll(reduceMap);
            }
            isRender = true;
        }
        return this;
    }

    private Map<String, TemplateModel> buildModel(Map<String, Object> params) throws TemplateModelException {
        Map<String, TemplateModel> models = new LinkedHashMap<>();
        ObjectWrapper objectWrapper = environment.getObjectWrapper();
        Environment.Namespace namespace = environment.getCurrentNamespace();
        Iterator<Map.Entry<String, Object>> iterator = params.entrySet().iterator();
        for (int i = 0; iterator.hasNext(); i++) {
            Map.Entry<String, Object> entry = iterator.next();
            if (i < templateModels.length) {
                templateModels[i] = objectWrapper.wrap(entry.getValue());
            } else {
                String key = entry.getKey();
                models.put(key, namespace.get(key));
                namespace.put(key, objectWrapper.wrap(entry.getValue()));
            }
        }
        return models;
    }


}
